#include "ParticleTracking.h"

ParticleTracking::ParticleTracking(
	const shared_ptr<CaseSettings> caseSettings0,
	const shared_ptr<ModelSettings>modelSettings0,
	const shared_ptr<Constants>constant0,
	const shared_ptr<Flowfield>flowfield0,
	const shared_ptr<WallGeometry>wallGeometry0)
{
	 totalP = GetPNumber(modelSettings0);
	 pAll_s = make_shared<vector<Particle>>(GetInjectionAll(caseSettings0, modelSettings0, constant0));
	 pAll_end = make_shared<vector<Particle>>(GetInjectionAll(caseSettings0, modelSettings0, constant0));
	 PAll_allTime = make_shared<vector<vector<Particle>>>(vector<vector<Particle>>(totalP, vector<Particle>()));

	 for (int pi = 0; pi < totalP; pi++)
	 {
		 /////////////////////////////////////////////////
		 /*part 1 Generate particle state at beginning  */
		 ////////////////////////////////////////////////
		 int pID = pi;
		 Particle p_start = (*pAll_s)[pID];
		 Particle p_in = p_start;
		 Particle p_temp = p_start;

		 PLinearMotion pLinear_in;
		 Flag pFlag;

		 /////////////////////////////////////////////////
		 /*part 2 Main particle tracking loop           */
		 ////////////////////////////////////////////////
		 int t = 0;
		 ImpingementParameter ImpingePara_in;
		 (*PAll_allTime)[pID].push_back(p_start); // start to add particle into the alldata

		 while (t < (*modelSettings0).numberIterationSteps && (pFlag.active))
		 {
			 /* Specify lastTime and current Time particle Info*/
			 Particle p_lastTime;

			 if (t == 0)
			 {
				 p_lastTime = p_start;
			 }
			 else if (t > 0)
			 {
				 p_lastTime = p_in; //particle state at the beginning of previous time step
				 p_in = p_temp;// particle state at the beginning of current time step
			 }
			 /* 2.1: get the flowfield data at current particle position*/
			 Cphase cphase_current(caseSettings0, constant0);
			 cphase_current.V[0] = (*flowfield0).GetGasVelocityX(p_in.pos[0]);
			 cphase_current.V[1] = (*modelSettings0).gVy;

			 /* 2.2: Update particle flag and scaling factor*/
			 pFlag.UpdateFlag();
			 p_in.UpdateSF(pFlag, p_lastTime, t);

			 /* 2.3: Particle wall impingement and rebound*/
			 //p_in.pos[0] = 0.5; p_in.pos[1] = 0.02;
			 WallImpingement wallImpingeTemp(pFlag, modelSettings0,
				 caseSettings0, p_in, wallGeometry0, ImpingePara_in);
			 ImpingePara_in = wallImpingeTemp.ImpingePara; //Update shortDist and BCtype
			 WallRebound pWallRebound(p_in, pFlag, wallGeometry0, ImpingePara_in);
			 p_in = pWallRebound.pOut;

			 /* 2.4: Particle tracking*/
			 // Get particle time step size
			 TimeStepSize pTimeStep(ImpingePara_in, p_in);
			 p_in.time = p_lastTime.time + pTimeStep.timeStepSize; //record accumulated particle time;
			 // Get slip and Rep
			 Slip pSlip(p_in, cphase_current);
			 // Get acceleration, cd, fd, tao,
			 PLinearMotion pLinear = PLinearMotion(pSlip, p_in, cphase_current);
			 // Get particle velocity and update position
			 MotionIntegration pMotion(p_in, pFlag, pLinear, pTimeStep);
			 p_temp = pMotion.pOut;
			 (*PAll_allTime)[pID].push_back(p_temp);
			 // Save the end status of particle
			 if (t == (*modelSettings0).numberIterationSteps - 1 || (!pFlag.active))
			 {
				 (*pAll_end)[pID] = p_temp;
			 }
			 // time advancing
			 t = t + 1;
		 } // end of time advancing for a particle
	 } //end of particle loop
}